// Copyright (c) [2019] [huazhonghua]
// [理解] is licensed under the Mulan PSL v1.
// You can use this software according to the terms and conditions of the Mulan PSL v1.
// You may obtain a copy of Mulan PSL v1 at:
//     http://license.coscl.org.cn/MulanPSL
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
// PURPOSE.
// See the Mulan PSL v1 for more details.

#include <stdlib.h>
#include <wchar.h>
#include <pthread.h>

#include <stdio.h>

#include "structure.h"
#include "labels.h"

Node* create_node_only() {
	Node* node = malloc(sizeof(Node));
	node->part = 0;
	node->table = NULL;
	node->other.pointer = NULL;
	return node;
}

void track_node(Node* all, Node* node) {
	Node* node_list = get_linked(all, NODE_LIST);
	Node* lock = get_linked(node_list, LOCK);
	pthread_mutex_t* mutex = lock->other.pointer;

	pthread_mutex_lock(mutex);

	add_to_list_node(node_list, node);

	pthread_mutex_unlock(mutex);
}

Node* create_track_node(Node* all) {
	Node* node = create_node_only();
	track_node(all, node);
	return node;
}

Node* create_node_no_track(Node* all) {
	return create_node_only();
}

List* create_list(int capacity) {
	List* list = malloc(sizeof(List));
	list->length = 0;
	list->capacity = capacity;
	list->nodes = calloc(list->capacity, sizeof(Node*));
	return list;
}

void free_list(List* list) {
	if(list == NULL) return;
	if(list->nodes != NULL) {
		free(list->nodes);
	}
	free(list);
}

void clear_list(List* list) {
	if(list == NULL) return;
	list->length = 0;
}

void add_to_list(List* list, Node* node) {
	int new_capacity = list->capacity;
	while(list->length >= new_capacity) {
		new_capacity *= 2;
	}

	if(new_capacity != list->capacity) {
		list->capacity = new_capacity;
		list->nodes = realloc(list->nodes, list->capacity * sizeof(Node*));
	}

	list->nodes[list->length++] = node;
}

Node* get_list_tail(List* list) {
	if(list == NULL || list->length <= 0) return NULL;
	return list->nodes[list->length - 1];
}

Node* get_list_node_tail(Node* node) {
	List* list = node->other.pointer;
	return get_list_tail(list);
}

Node* get_list_node_head(Node* node) {
	List* list = node->other.pointer;
	if(list == NULL || list->length <= 0) return NULL;
	return list->nodes[0];
}

Bucket* create_bucket() {
	Bucket* bucket = malloc(sizeof(Bucket));
	bucket->length = 0;
	bucket->capacity = 1;
	bucket->links = calloc(bucket->capacity, sizeof(Link*));
	return bucket;
}

int find_link_in_bucket(Bucket* bucket, int part) {
	int i;
	for(i = 0; i < bucket->length; ++i) {
		Link* link = bucket->links[i];

		if(link->part == part) {
			return i;
		}
	}
	return -1;
}

int set_bucket_link(Bucket* bucket, Link* link) {
	int new_capacity = bucket->capacity;

	while(bucket->length >= new_capacity) {
		new_capacity *= 2;
	}

	if(new_capacity != bucket->capacity) {
		bucket->capacity = new_capacity;
		bucket->links = realloc(bucket->links, bucket->capacity * sizeof(Link*));
	}

	int i = find_link_in_bucket(bucket, link->part);

	if(i >= 0) {
		Link* old_link = bucket->links[i];
		old_link->target = link->target;

		free(link);
		return 0;
	}
	else {
		bucket->links[bucket->length++] = link;
		return 1;
	}
}

Table* create_table() {
	Table* table = malloc(sizeof(Table));
	table->link_count = 0;
	table->buckets_size = 1;
	table->buckets = calloc(table->buckets_size, sizeof(Bucket*));
	return table;
}

Bucket* get_part_bucket(Table* table, int part) {
	unsigned i = ((unsigned)part) % table->buckets_size;
	return table->buckets[i];
}

Link* get_table_link(Table* table, int part) {
	Bucket* bucket = get_part_bucket(table, part);
	if(bucket == NULL) return NULL;
	int j = find_link_in_bucket(bucket, part);
	return j < 0 ? NULL : bucket->links[j];	
}

void remove_table_link(Table* table, int part) {
	unsigned i = ((unsigned)part) % table->buckets_size;
	Bucket* bucket = table->buckets[i];
	if(bucket == NULL) return;

	int j = find_link_in_bucket(bucket, part);
	if(j < 0) return;

	Link* link = bucket->links[j];
	--(bucket->length);
	--(table->link_count);
	bucket->links[j] = bucket->links[bucket->length];
	free(link);
}

Node* get_table_node(Table* table, int part) {
	Link* link = get_table_link(table, part);
	return link == NULL ? NULL : link->target;
}

Node* get_table_node_by_parts(Table* table, int* parts, int parts_length) {
	Table* current_table = table;
	Node* node = NULL;
	for(int i = 0; i < parts_length; ++i) {
		if(current_table == NULL) return NULL;
		node = get_table_node(current_table, parts[i]);
		if(node == NULL) return NULL;
		current_table = node->table;
	}
	return node;
}

Node* get_node_by_parts(Node* node, int* parts, int parts_length) {
	if(node == NULL) {
		return NULL;
	}
	return get_table_node_by_parts(node->table, parts, parts_length);
}

Node* get_chained(Node* node, int* parts, int parts_length) {
	if(node == NULL) {
		return NULL;
	}
	return get_table_node_by_parts(node->table, parts, parts_length);
}

Node* get_node_by_part_list(Node* node, List* part_list) {
	Node* current_node = node;
	Table* current_table = NULL;
	for(int i = 0; i < part_list->length; ++i) {
		if(current_node == NULL) return NULL;
		current_table = current_node->table;
		if(current_table == NULL) return NULL;
		current_node = get_table_node(current_table, part_list->nodes[i]->other.c_int);
	}
	return current_node;
}

Node* get_linked(Node* node, int part) {

if(node == NULL) {
	printf("get linked %d NULL\n", part);
}

	if(node->table == NULL) return NULL;
	return get_table_node(node->table, part);
}

int is_overloaded(int link_count, int buckets_size) {
	return link_count > 0 && (double)link_count / buckets_size > 0.8;
}

int set_buckets_link(Bucket** buckets, int buckets_size, Link* link) {
	unsigned i = ((unsigned)link->part) % buckets_size;
	Bucket* bucket = buckets[i];
	if(bucket == NULL) {
		bucket = create_bucket();
		buckets[i] = bucket;
	}
	return set_bucket_link(bucket, link);
}

void resize_table(Table* table) {
	int new_buckets_size = table->buckets_size;
	while(is_overloaded(table->link_count, new_buckets_size)) {
		new_buckets_size *= 2;
	}
	if(new_buckets_size == table->buckets_size) return;

	Bucket** new_buckets = calloc(new_buckets_size, sizeof(Bucket*));
	int i, j;

	for(i = 0; i < table->buckets_size; ++i) {
		Bucket* bucket = table->buckets[i];
		if(bucket == NULL) continue;
		for(j = 0; j < bucket->length; ++j) {
			Link* link = bucket->links[j];
			set_buckets_link(new_buckets, new_buckets_size, link);
		}
	}

	free(table->buckets);
	table->buckets = new_buckets;
	table->buckets_size = new_buckets_size;
}

void set_table_node(Table* table, int part, Node* node) {
	if(is_overloaded(table->link_count, table->buckets_size)) {
		resize_table(table);
	}

	Link* link = malloc(sizeof(Link));
	link->part = part;
	link->target = node;

	if(set_buckets_link(table->buckets, table->buckets_size, link)) {
		++(table->link_count);
	}
}

void link_node(Node* node, int part, Node* target) {
	if(node->table == NULL) {
		node->table = create_table();
	}
	set_table_node(node->table, part, target);
}

void unlink_node(Node* node, int part) {
	if(node->table != NULL) {
		remove_table_link(node->table, part);
	}
}

void make_list_node(Node* all, Node* node, int capacity) {
	node->other.pointer = create_list(capacity);
	link_node(node, TYPE, get_linked(all, LIST));
}

Node* create_list_node(Node* all, int capacity) {
	Node* node = create_node(all);
	make_list_node(all, node, capacity);
	return node;
}

Node* get_create_part_node(Node* all, Node* node, int part) {
	Node* part_node = get_linked(node, part);
	if(part_node == NULL) {
		part_node = create_node(all);
		part_node->part = part;
		link_node(node, part, part_node);
	}
	return part_node;
}

Node* get_create_node(Node* all, Node* node, int* parts, int parts_length) {
	Node* current_node = node;

	for(int i = 0; i < parts_length; ++i) {
		current_node = get_create_part_node(all, current_node, parts[i]);
	}

	return current_node;
}

void set_node(Node* node, int path, Node* target) {
	if(node->table == NULL) {
		node->table = create_table();
	}
	set_table_node(node->table, path, target);
}

int get_next_instance_part(Node* instances) {
	if(instances->table == NULL) {
		instances->table = create_table();
	}
	return instances->table->link_count;
}

Node* create_instance(Node* all, Node* type) {
	Node* instances = get_linked(type, INSTANCE);
	if(instances == NULL) {
		instances = create_node(all);
		link_node(instances, TYPE, get_linked(all, INSTANCE));
		link_node(type, INSTANCE, instances);
	}

	int part = get_next_instance_part(instances);
	Node* node = create_node(all);
	node->part = part;
	link_node(instances, part, node);
	link_node(node, TYPE, type);
	return node;
}

void add_to_list_node(Node* list_node, Node* node) {
	List* list;
	if(list_node->other.pointer == NULL) {
		list = create_list(1);
		list_node->other.pointer = list;
	}
	else {
		list = list_node->other.pointer;
	}
	add_to_list(list, node);
}

Node* get_list_node_item(Node* list_node, int i) {
	List* list = list_node->other.pointer;
	if(list == NULL || i >= list->length) return NULL;
	return list->nodes[i];
}

void set_list_node_item(Node* list_node, int index, Node* node) {
	List* list = list_node->other.pointer;
	while(list->length < index + 1) {
		add_to_list(list, NULL);
	}
	list->nodes[index] = node;
}

int get_list_node_length(Node* list_node) {
	List* list = list_node->other.pointer;
	if(list == NULL) return 0;
	return list->length;
}

Node* clear_list_node(Node* list_node) {
	clear_list(list_node->other.pointer);
}

void free_bucket(Bucket* bucket) {
	if(bucket == NULL) return;
	for(int i = 0; i < bucket->length; ++i) {
		Link* link = bucket->links[i];
		if(link != NULL) {
			free(link);
		}
	}
	free(bucket->links);
	free(bucket);
}

void free_table(Table* table) {
	if(table == NULL) return;
	for(int i = 0; i < table->link_count; ++i) {
		Bucket* bucket = table->buckets[i];
		free_bucket(bucket);
	}
	free(table->buckets);
	free(table);
}

void free_node(Node* node) {
/*	Node* free_count = get_linked(node, FREE);
	if(free_count == NULL) {
		free_count = create_node_only();
		free_count->part = 0;
		link_node(node, FREE, free_count);
	}
	++(free_count->part);*/

	if(node == NULL) return;
	free_table(node->table);
	free(node);
}

void free_list_node(Node* node) {
	free_list(node->other.pointer);
	free_node(node);
}

int get_link_count(Node* node) {
	if(node->table == NULL) return 0;
	return node->table->link_count;
}

void iterate_links(Node* node, int (*handle_link)(void* state, Link* link), void* state) {
	if(node->table == NULL) return;

	Table* table = node->table;
	for(int i = 0; i < table->buckets_size; ++i) {
		Bucket* bucket = table->buckets[i];
		if(bucket != NULL) {
			for(int j = 0; j < bucket->length; ++j) {
				Link* link = bucket->links[j];
				if(!handle_link(state, link)) return;
			}
		}
	}
}

struct StateFunc {
	void* state;
	void* func;
};

int handle_target(void* state, Link* link) {
	struct StateFunc* state_func_p = state;
	int (*handle_node)(Node* state, Node* node) = state_func_p->func;
	return handle_node(state_func_p->state, link->target);
}

void iterate_linked(Node* node, int (*handle_node)(void* state, Node* node), void* state) {
	struct StateFunc state_func;
	state_func.state = state;
	state_func.func = handle_node;

	iterate_links(node, handle_target, &state_func);
}
